package com.zhen.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.ReflectionUtils.MethodFilter;

import com.zhen.process.StringProcess;

@Component
public class SpringAnnotationScanner implements BeanPostProcessor{

    private static final Logger log = LoggerFactory.getLogger(SpringAnnotationScanner.class);

    private final Class<? extends Annotation> annotationClass = ProcessMethod.class;
    private final Class<? extends Annotation> paramAnnotationClass = ProcessParam.class;
    
    private List<ProcessBean> classList;
    
    private Map<String, Object> beanMap;
    private Class<? extends Object>  originalBeanClass;
//	private ApplicationContext context;
    
    
    public SpringAnnotationScanner() {
    	classList = new ArrayList<ProcessBean>();
    	beanMap = new HashMap<String, Object>();
	}

	@Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        /*if (localProcessBean != null) {
        	classList.add(localProcessBean);
            log.info("{} bean listeners added", beanName);
            localProcessBean = null;
        }*/
		
		if(originalBeanClass != null){
			beanMap.put(beanName, bean);
		}
		originalBeanClass = null;
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//        executeMethod(bean, beanName, add);
    	classList.clear();
    	final AtomicBoolean add = executeMethod(bean, beanName);
        

        if (add.get()) {
            originalBeanClass = bean.getClass();
        }
        return bean;
    }

	private AtomicBoolean executeMethod(Object bean, String beanName) {
		
		final AtomicBoolean add = new AtomicBoolean();
		ReflectionUtils.doWithMethods(bean.getClass(),
                new MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException,
            IllegalAccessException {
                add.set(true);
            }
        },
        new MethodFilter() {
            @Override
            public boolean matches(Method method) {
                
                if (method.isAnnotationPresent(annotationClass)) {
                	ProcessMethod annotation = (ProcessMethod)method.getAnnotation(annotationClass);
                	String name = annotation.name();
                	
                	Annotation[][] parameterAnnotations = method.getParameterAnnotations();
                	
                	ProcessBean processBean = new ProcessBean();
                	processBean.setBeanName(beanName);
                	processBean.setMethodName(method.getName());;
                	processBean.setName(annotation.name());
                	Parameter[] parameters = method.getParameters();
                	for(Parameter parameter : parameters){
                		String processResult = null;
                		ProcessParam annotation2 = (ProcessParam)parameter.getAnnotation(paramAnnotationClass);
                		
                		if(StringUtils.isNotBlank(annotation2.process())){
                			try {
								Method method2 = bean.getClass().getMethod(annotation2.process(),null);
								Object invoke = method2.invoke(bean);
								
								if(!(invoke instanceof String)){
									log.error(String.format("%s参数处理器类型必须为String类型", annotation2.process()));
								}else{
									if(invoke != null){
										
										processResult = (String)invoke;
									}
								}
								
							} catch (NoSuchMethodException e) {
								log.error("没有找到参数处理器{}",e.getMessage(),e);
							} catch (SecurityException e) {
								log.error("获取参数处理器异常{}",e.getMessage(),e);
							} catch (IllegalAccessException e) {
								log.error("执行参数处理器异常{}",e.getMessage(),e);
							} catch (IllegalArgumentException e) {
								log.error("执行参数处理器异常{}",e.getMessage(),e);
							} catch (InvocationTargetException e) {
								log.error("执行参数处理器异常{}",e.getMessage(),e);
							}
                		};
                		processBean.addHtml(new HtmlBean(annotation2.name(), annotation2.html().code,annotation2.html().type,parameter.getType().getTypeName(),processResult));
                	}
                	classList.add(processBean);
                    return true;
                }
                
                return false;
            }
        });
		return add;
	}

	/*@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		context = applicationContext;
	}*/

	public List<ProcessBean> getClassList() {
		classList.clear();
		Set<String> keySet = beanMap.keySet();
		Object obj;
		for(String key : keySet){
			obj = beanMap.get(key);
			executeMethod(obj, key);
		}
		
		return classList;
	}
	
	public Map<String, Object> getBeanMap(){
		return beanMap;
	}
	
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
		System.out.println(StringProcess.class.getMethod("key", int.class));
	}
}
